<?php
namespace App\Services;

//引入两个类
use App\Enum\CityEnableEnum;
use App\Enum\CityGpsEnum;
use App\Enum\RedisEnum;
use App\Models\CityGpsModel;
use Illuminate\Support\Facades\Log;
use Location\Coordinate;
use Location\Polygon;
use Exception;

class GeoService extends BaseService
{
    /**
     * @param array $points['纬度，经度', ....]
     * @param string $cityCode 城市CODE
     * @param integer $rowId 是否某个记录单独验证
     * @return bool 点可用true， 点不可用false
     * @throws Exception
     */
    public static function isPointsAvailable(array $points, string $cityCode=CityEnableEnum::MUNICIPALITY_CITY_SHANGHAI, int $rowId=0): bool
    {
        try{
            $redisKey = sprintf(RedisEnum::R_CITY_MAP_FENCE_KEY, $cityCode );
            $params = [
                'city_code' => $cityCode,
                'row_id'    => $rowId
            ];
            $polygons = parent::_cacheableData($redisKey, RedisEnum::R_CITY_MAP_FENCE_TO,(new GeoService()), '_getCityGpsAreas', $params );
            foreach( $points as $point ) {
                $result = self::_isPointAvailable($point, $polygons);
                if( $result === false) {
                    return false;
                }
            }
            return true;
        }catch (Exception $e){
            throw new Exception("验证点在区域内，".$e->getMessage());
        }
    }

    /**
     * 验证一个点 与城市围栏的关系
     *
     * @param $point
     * @param $polygons
     * @return bool
     * @throws Exception
     */
    private static function _isPointAvailable($point, $polygons): bool
    {
        try{
            $lat = explode(',',$point)[0];
            $lng = explode(',',$point)[1];
            $testPoint = new Coordinate($lat, $lng);
            $red = 0;
            $green = 0;
            $defaultStatus = true;
            $zoneName='';

            foreach( $polygons as $row){
                $polygon = json_decode($row['polygon'],true);
                $zoneName = $row['zone_name'];

                // 此点，确保一个城市的限制状态是统一的。
                if( $defaultStatus === true ) $defaultStatus = $row['forbidden'];

                $geo = new Polygon();
                foreach ($polygon as $gps) $geo->addPoint(new Coordinate($gps[0],$gps[1]));
                if( $geo->contains($testPoint)  ) {
                    if( $defaultStatus == CityGpsEnum::CITY_GPS_FORBIDDEN_GREEN ) { // 围栏 许可
                        $green ++; break;
                    }
                    if( $defaultStatus == CityGpsEnum::CITY_GPS_FORBIDDEN_RED ) { // 围栏 禁止
                        $red ++; break;
                    }
                }
            }
            Log::notice(sprintf("围栏验证：green=%d, red=%d, rule=%d", $green, $red,$defaultStatus));
            if( $green > 0 ) return true;
            if( $red > 0 ) throw new Exception($zoneName);
            // 当点不在围栏中
            if( $defaultStatus === true ) return true;
            else return !($defaultStatus == CityGpsEnum::CITY_GPS_FORBIDDEN_GREEN);
        }catch (Exception $e){
            throw new Exception(sprintf("围栏（%s）",$e->getMessage()));
        }
    }

    /**
     * 获取城市的围栏
     *
     * @param array $params
     * @return array
     * @throws Exception
     */
    public static function _getCityGpsAreas(array $params): array
    {
        try {
            $cityCode = $params['city_code'];
            $rowId = $params['row_id'];
            $builder = CityGpsModel::query()->where(['city_code' => $cityCode]);

            // 如果是某个记录，则不需要验证状态
            if( !empty($rowId) ) $builder->where(['id'=>$rowId]);
            else $builder->where(['status' => CityGpsEnum::CITY_GPS_STATUS_NORMAL]);

            return $builder->select('forbidden','zone_name', 'polygon')->get()->toArray();
        }catch (Exception $e ){
            throw new Exception('获取城市区域失败');
        }
    }
}
